﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace Spinnerets.Network
{
    using Instrumentation;
    using System.ComponentModel;
    using System.Net.NetworkInformation;
    using System.Net.Sockets;
    using System.Threading;
    using System.Management;
    using System.Diagnostics;
    using System.Runtime.InteropServices;

    /// <summary>
    /// Executes local network scan for a specified address range.
    /// </summary>
    public class NetworkScanner
    {
        NetworkAddress _startAddress, _endAddress;
        ulong _addressCount = 0, _replyCount = 0;

        /// <summary>
        /// Occurs when a device is found on the network.
        /// </summary>
        public event EventHandler<DeviceFoundArgs> DeviceFound;

        /// <summary>
        /// Initializes a new instance of the <see cref="Scanner"/> class.
        /// </summary>
        /// <param name="startAddress">The start address.</param>
        /// <param name="endAddress">The end address.</param>
        public NetworkScanner(IPAddress startAddress, IPAddress endAddress)
        {
            _startAddress = new NetworkAddress(startAddress);
            _endAddress = new NetworkAddress(endAddress);
        }

        /// <summary>
        /// Executes a network scan for the provided start and end addresses.
        /// </summary>
        public void Execute()
        {
            while (_startAddress <= _endAddress)
            {
                Ping ping = new Ping();
                ping.PingCompleted += new PingCompletedEventHandler(PingCompleted);
                ping.SendAsync(_startAddress.CoreAddress, ping);

                _addressCount++;
                _startAddress++;
            }

            while (_replyCount < _addressCount)
                Thread.Sleep(1500);
        }

        /// <summary>
        /// Handles the PingCompleted event of a <see cref="Ping"/> request.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Net.NetworkInformation.PingCompletedEventArgs"/> instance containing the event data.</param>
        void PingCompleted(object sender, PingCompletedEventArgs e)
        {
            if (e.Reply != null && DeviceFound != null)
            {
                DeviceFound(sender, new DeviceFoundArgs(e.Reply));
                //ScanPorts(e.Reply.Address);
            }

            _replyCount++;
        }

        /*
        const int _bufferSize = 1024;
        StringBuilder _openPorts = new StringBuilder();
        long _activePortScans = 0;

        private void ScanPorts(IPAddress ipAddress)
        {
            DateTime startTime = DateTime.Now;

            if (!UseCommonPorts)
            {
                int currentPort = IPEndPoint.MinPort;
                while (currentPort <= IPEndPoint.MaxPort)
                {
                    IPEndPoint currentEndPoint = new IPEndPoint(ipAddress, currentPort);

                    BackgroundWorker connectWorker = new BackgroundWorker();
                    connectWorker.DoWork += new DoWorkEventHandler(connectWorker_DoWork);

                    connectWorker.RunWorkerAsync(currentEndPoint);
                    _activePortScans++;

                    if (currentPort + 1 <= IPEndPoint.MaxPort)
                        currentPort++;
                    else
                        break;
                }
            }
            else
            {
                _portList.ForEach(
                    delegate(int port)
                    {
                        IPEndPoint currentEndPoint = new IPEndPoint(ipAddress, port);

                        BackgroundWorker connectWorker = new BackgroundWorker();
                        connectWorker.DoWork += new DoWorkEventHandler(connectWorker_DoWork);

                        connectWorker.RunWorkerAsync(currentEndPoint);
                        _activePortScans++;
                    }
                );
            }

            while (_activePortScans > 0)
                Thread.Sleep(1000);

            TimeSpan totalRuntime = DateTime.Now - startTime;
        }
         */

        void connectWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            IPEndPoint endPoint = (IPEndPoint)e.Argument;

            using (Socket socket = new Socket(endPoint.AddressFamily, SocketType.Raw, ProtocolType.Tcp))
            {
                //socket.
            }

            using (TcpClient tcpClient = new TcpClient(endPoint.AddressFamily))
            {
                tcpClient.NoDelay = true;
                tcpClient.ReceiveTimeout = 30;
                tcpClient.SendTimeout = 30;

                try
                {
                    tcpClient.Connect(endPoint);
                    //_openPorts.AppendFormat("{0}:\topen", endPoint.Port).AppendLine();

                    //byte[] responseBuffer = new byte[_bufferSize];
                    //StringBuilder responseMessage = new StringBuilder();

                    //using (NetworkStream netStream = tcpClient.GetStream())
                    //{
                    //    int readBytes = _bufferSize;
                    //    while (readBytes == _bufferSize)
                    //    {
                    //        readBytes = netStream.Read(responseBuffer, 0, _bufferSize);
                    //        responseMessage.Append(Encoding.ASCII.GetString(responseBuffer.ToArray()));
                    //    }
                    //}
                }
                catch (Exception)
                {
                    // Should this get logged? Nmap doesn't log it.
                }
            }

            //_activePortScans--;
        }
    }
}
